| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- 'use client';
- import { useState, useEffect } from 'react';
- import { fetchApi } from '@/lib/utils/client';
- import './donation-modal.scss';
- type CrewMemberInfo = {
- crewMemberID: number;
- nickname: string;
- thumb: string|null;
- channelName: string|null;
- };
- type ActiveCrew = {
- crewSessionID: number;
- title: string;
- crewName: string;
- members: CrewMemberInfo[];
- }|null;
- type Props = {
- channelSID: string;
- onClose: () => void;
- };
- export default function DonationModal({ channelSID, onClose }: Props)
- {
- const [amount, setAmount] = useState(1000);
- const [message, setMessage] = useState('');
- const [sendName, setSendName] = useState('');
- const [activeCrew, setActiveCrew] = useState<ActiveCrew>(null);
- const [selectedMember, setSelectedMember] = useState<number|null>(null);
- const [sending, setSending] = useState(false);
- const [done, setDone] = useState(false);
- const presetAmounts = [1000, 3000, 5000, 10000, 30000, 50000];
- useEffect(() => {
- // 활성 크루 세션 조회
- fetchApi<ActiveCrew>(`/api/donation/crew/active/${channelSID}`)
- .then(res => {
- if (res.data) setActiveCrew(res.data);
- })
- .catch(() => {});
- }, [channelSID]);
- const handleSend = async () => {
- if (amount < 1000) {
- alert('최소 후원 금액은 1,000원입니다.');
- return;
- }
- if (!sendName.trim()) {
- alert('보내는 사람 이름을 입력해 주세요.');
- return;
- }
- setSending(true);
- try {
- const body: Record<string, unknown> = {
- channelSID,
- amount,
- message: message || null,
- sendName: sendName.trim()
- };
- if (activeCrew && selectedMember) {
- body.crewSessionID = activeCrew.crewSessionID;
- body.crewMemberID = selectedMember;
- }
- await fetchApi('/api/donation/send', {
- method: 'POST',
- body
- });
- setDone(true);
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : '후원에 실패했습니다.');
- } finally {
- setSending(false);
- }
- };
- if (done) {
- return (
- <div className="donation-modal">
- <div className="donation-modal__overlay" onClick={onClose} />
- <div className="donation-modal__box">
- <div className="donation-modal__done">
- <div className="donation-modal__done-icon">🎉</div>
- <p className="donation-modal__done-text">{amount.toLocaleString()}원 후원 완료!</p>
- <button type="button" className="donation-modal__btn donation-modal__btn--primary" onClick={onClose}>닫기</button>
- </div>
- </div>
- </div>
- );
- }
- return (
- <div className="donation-modal">
- <div className="donation-modal__overlay" onClick={onClose} />
- <div className="donation-modal__box">
- <div className="donation-modal__header">
- <h2 className="donation-modal__title">후원하기</h2>
- <button type="button" className="donation-modal__close" onClick={onClose}>×</button>
- </div>
- {/* 보내는 사람 */}
- <div className="donation-modal__field">
- <label>닉네임</label>
- <input type="text" value={sendName} onChange={e => setSendName(e.target.value)} placeholder="보내는 사람" maxLength={20} />
- </div>
- {/* 금액 */}
- <div className="donation-modal__field">
- <label>금액</label>
- <div className="donation-modal__presets">
- {presetAmounts.map(a => (
- <button
- type="button"
- key={a}
- className={`donation-modal__preset${amount === a ? ' donation-modal__preset--active' : ''}`}
- onClick={() => setAmount(a)}
- >
- {a.toLocaleString()}원
- </button>
- ))}
- </div>
- <input
- type="number"
- min={1000}
- max={10000000}
- step={1000}
- value={amount}
- onChange={e => setAmount(Number(e.target.value))}
- />
- </div>
- {/* 메시지 */}
- <div className="donation-modal__field">
- <label>메시지 (선택)</label>
- <textarea value={message} onChange={e => setMessage(e.target.value)} placeholder="응원 메시지를 남겨주세요" maxLength={100} rows={2} />
- </div>
- {/* 크루원 선택 */}
- {activeCrew && activeCrew.members.length > 0 && (
- <div className="donation-modal__crew">
- <label className="donation-modal__crew-label">
- 크루원에게 후원 <span className="donation-modal__crew-tag">{activeCrew.crewName}</span>
- </label>
- <div className="donation-modal__crew-list">
- <button
- type="button"
- className={`donation-modal__crew-item${selectedMember === null ? ' donation-modal__crew-item--active' : ''}`}
- onClick={() => setSelectedMember(null)}
- >
- <div className="donation-modal__crew-thumb donation-modal__crew-thumb--default">채널</div>
- <span>채널 주인</span>
- </button>
- {activeCrew.members.map(m => (
- <button
- type="button"
- key={m.crewMemberID}
- className={`donation-modal__crew-item${selectedMember === m.crewMemberID ? ' donation-modal__crew-item--active' : ''}`}
- onClick={() => setSelectedMember(m.crewMemberID)}
- >
- {m.thumb ? (
- <img src={m.thumb} alt="" className="donation-modal__crew-thumb" />
- ) : (
- <div className="donation-modal__crew-thumb donation-modal__crew-thumb--default">{m.nickname.charAt(0)}</div>
- )}
- <span>{m.nickname}</span>
- </button>
- ))}
- </div>
- </div>
- )}
- {/* 전송 */}
- <div className="donation-modal__footer">
- <button type="button" className="donation-modal__btn" onClick={onClose}>취소</button>
- <button type="button" className="donation-modal__btn donation-modal__btn--primary" onClick={handleSend} disabled={sending}>
- {sending ? '전송 중...' : `${amount.toLocaleString()}원 후원`}
- </button>
- </div>
- </div>
- </div>
- );
- }
|